#include "Utils.h"
#include "NetWork.h"
#include "Worker.h"
#include "MysqlFunc.h"
#include "ThreadPool.h"
#define TIME 30
int exit_pipe[2];

typedef struct MainNode_s
{
    struct MainNode_s* next;
    int net_fd;
    int pwd_id;
    int uid;
    int sign_flag;
    int login_flag;
}MainNode;
typedef struct
{
    MainNode* rear;
    MainNode* front;
    int queue_size;
}MainQueue;
typedef struct
{
    MainQueue mq[TIME];
    int signup_flag;
    int login_flag;
}Main;
int MainInit(Main* main)
{
    for(int i =0;i<TIME;i++)
    {
        bzero(&main->mq[i], sizeof(MainQueue));
    }
    return 0;
}
int EnMain(MainQueue* queue,int net_fd)
{
    MainNode*new_node = (MainNode *)calloc(1, sizeof(MainNode));
    new_node->net_fd = net_fd;
    new_node->uid = -1;
    new_node->pwd_id = 0;
    new_node->sign_flag = 0;
    new_node->login_flag = 0;
    if(queue->queue_size == 0){
        queue->front = new_node;
    }else{
        queue->rear->next = new_node;
    }
    queue->rear = new_node;
    ++queue->queue_size;
    return 0;
}
int ReEnMain(MainQueue* queue,MainQueue* newqueue,int net_fd)
{
    MainNode* cur = queue->front;
    MainNode *temp = NULL;
    while(1){
        if(cur->net_fd == net_fd)
        {
            break;
        }
        temp = cur;
        cur = cur->next;
    }
    if(temp != NULL)
    {
        temp->next = cur->next;
    }
    if(queue->front == cur)
    {
        queue->front =cur->next;
    }
    if(queue->rear == cur && temp != NULL)
    {
        queue->rear = temp;
    }
    if(queue->queue_size == 1){
        queue->front =NULL;
        queue->rear = NULL;
    }
    int pwd_id = cur->pwd_id;
    int uid =cur->uid;
    free(cur);
    --queue->queue_size;
    
    MainNode*new_node = (MainNode *)calloc(1, sizeof(MainNode));
    new_node->net_fd = net_fd;
    new_node->uid = uid;
    new_node->pwd_id = pwd_id;
    new_node->login_flag = 0;
    new_node->sign_flag = 0;
    if(newqueue->queue_size == 0){
        newqueue->front = new_node;
    }else{
        newqueue->rear->next = new_node;
    }
    newqueue->rear = new_node;
    ++newqueue->queue_size;
    return 0;
}
int Getpwd(MainQueue* queue,int net_fd,int* pwd_id)
{
    MainNode* cur = queue->front;
    while(cur !=NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    *pwd_id = cur->pwd_id;
    return 0;
}
int Getuid(MainQueue* queue,int net_fd,int* uid)
{
    MainNode* cur = queue->front;
    while(cur!=NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    *uid = cur->uid;
    return 0;
}
int Getsign(MainQueue* queue,int net_fd,int* sign)
{
    MainNode* cur = queue->front;
    while(cur!=NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    *sign = cur->sign_flag;
    return 0;
}
int Getlogin(MainQueue* queue,int net_fd,int* login)
{
    MainNode* cur = queue->front;
    while(cur!=NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    *login = cur->login_flag;
    return 0;
}
int Setsign(MainQueue* queue,int net_fd,int sign)
{
    MainNode* cur = queue->front;
    while(cur != NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    cur->sign_flag = sign;
    return 0;
}
int Setlogin(MainQueue* queue,int net_fd,int login)
{
    MainNode* cur = queue->front;
    while(cur != NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    cur->login_flag = login;
    return 0;
}

int Setpwd(MainQueue* queue,int net_fd,int pwd_id)
{
    MainNode* cur = queue->front;
    while(cur != NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    cur->pwd_id = pwd_id;
    return 0;
}
int Setuid(MainQueue* queue,int net_fd,int uid)
{
    MainNode* cur = queue->front;
    while(cur != NULL)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        cur = cur->next;
    }
    cur->uid = uid;
    return 0;
}
int DeMain(MainQueue* queue,int net_fd)
{
    MainNode* cur = queue->front;
    MainNode *temp = NULL;
    while(1)
    {
        if(cur->net_fd == net_fd)
        {
            break;
        }
        temp = cur;
        cur = cur->next;
    }
    if(temp != NULL)
    {
        temp->next = cur->next;
    }
    if(queue->front == cur)
    {
        queue->front =cur->next;
    }
    if(queue->rear == cur && temp != NULL)
    {
        queue->rear = temp;
    }
    if(queue->queue_size == 1){
        queue->front =NULL;
        queue->rear = NULL;
    }
    free(cur);
    --queue->queue_size;
    return 0;
}
int DestoryQueue(MainQueue* queue,int* pair,int epoll_fd)
{
    if(queue == NULL)
    {
        printf("stack = NULL\n");
        return 1;
    }
    MainNode* pCur = queue->front;
    MainNode* pAfter = NULL;
    int net_fd;
    if(pCur !=NULL)
    {
        net_fd =pCur->net_fd;
    }
    while(pCur != NULL)
    {
        pAfter = pCur->next;
        pair[net_fd] = -1;
        EpollDel(epoll_fd,net_fd);
        close(net_fd);
        printf("time over,close client\n");
        free(pCur);
        queue->queue_size--;
        pCur = pAfter;
        if(pCur != NULL)
        {
            net_fd =pCur->net_fd;
        }
        queue->front = pCur;
    }
    queue->rear = NULL;
    return 0;

}

void UserSignalHandler(int signum){
    printf("signum = %d\n", signum);
    write(exit_pipe[1],"1",1);
}

int ExitHandler(ThreadPool *thread_pool){
    printf("threadPool is going to exit!\n");
    pthread_mutex_lock(&thread_pool->mutex);
    thread_pool->exit_flag = 1;
    pthread_cond_broadcast(&thread_pool->cond);
    pthread_mutex_unlock(&thread_pool->mutex);
    for(int j = 0; j < thread_pool->tid_arr.worker_num; ++j){
        pthread_join(thread_pool->tid_arr.arr[j],NULL);
    }
    printf("main thread is going to exit!\n");
    exit(0);
}

int ServerSignUpHandler_before(int net_fd,MainQueue* queue)
{
    //读一次，获取uname
    char user_name[32] = {0};
    RecvTrain(net_fd, user_name);
    //获取uid
    int uid = atoi(user_name);
    Setuid(queue,net_fd,uid);
    return 0;
}
int ServerSignUpHandler(MYSQL *mysql_connect, int net_fd,MainQueue* queue){

    char user_name[32] = {0};
    int uid;
    Getuid(queue,net_fd,&uid);
    sprintf(user_name,"%d",uid);
    //收密码
    char pwd[32] = {0};
    RecvTrain(net_fd, pwd);

    //返回状态
    char *ret = "1";
    SendTrain(net_fd, ret);

    //创建盐值，加密密码存储
    char salt[32] = {0};
    CreateSalt(salt);
    char md5_value[64];
    Md5(pwd, salt, md5_value);

    //用户表创建表项
    if(!(strcmp(user_name, "") && strcmp(pwd, ""))){
        return 1;
    }
    char sql[255] = {0};
    sprintf(sql, "insert into user_table(user_name, salt, encrypted_pwd) values ('%s', '%s', '%s');", user_name, salt, md5_value);
    SqlHandler(mysql_connect, sql, wr_sql, NULL);
    //虚拟文件表创建家目录表项
    bzero(sql, strlen(sql));
    sprintf(sql, "insert into virtual_file_system_table(file_name, user_name, path, type, tomb) values ('%s', '%s', '%s', 0, 0);", user_name, user_name, user_name);
    SqlHandler(mysql_connect, sql, wr_sql, NULL);
    //将根目录的父母id置为自己的
    bzero(sql, strlen(sql));
    sprintf(sql, "UPDATE virtual_file_system_table SET parent_id = file_id WHERE user_name = '%s';", user_name);
    SqlHandler(mysql_connect, sql, wr_sql, NULL);
    return 0;
}

int ServerLogInHandler_before(int net_fd,MainQueue* queue)
{
    printf("start log in \n");
    //读一次，获取uname
    char uname_buf[32] = {0};
    RecvTrain(net_fd, uname_buf);
    //获取uid
    int uid = atoi(uname_buf);
    Setuid(queue,net_fd,uid);
    Getuid(queue,net_fd,&uid);
    return 0;
}
int ServerLogInHandler(MYSQL *mysql_connect, int net_fd,MainQueue* queue){
    char uname_buf[32] = {0};
    int uid;
    Getuid(queue,net_fd,&uid);
    sprintf(uname_buf,"%d",uid);
    
    char sentence[255]={0};
    sprintf(sentence,"select salt from user_table where user_name = '%s'",uname_buf);
    char result[100]={0};
    SqlHandler(mysql_connect,sentence,0,result);
    char salt[21]={0};
    memcpy(salt,result,strlen(result));
    SendTrain(net_fd,salt);
    //获取密文，提取用户名比较是否正确
    bzero(result,sizeof(result));
    RecvTrain(net_fd,result);
    bzero(&sentence,sizeof(sentence));
    sprintf(sentence,"select user_name from user_table where encrypted_pwd = '%s'",result);
    char name[48]={0};
    SqlHandler(mysql_connect,sentence,0,name);
    int ret = memcmp(uname_buf,name,strlen(uname_buf));
    
    //处理登录结果
    char fret ='0';
    if(ret != 0){
        printf("netfd = %d, uid = %d, log error\n", net_fd, uid);
        SendTrain(net_fd, &fret);
        return 1;
    }
    fret = '1';
    SendTrain(net_fd, &fret);
    //将连接发给子线程
    char pwd[8]={0};
    bzero(&sentence,sizeof(sentence));
    sprintf(sentence,"select file_id from virtual_file_system_table where path = '%s' and file_name ='%s'",uname_buf,uname_buf);
    SqlHandler(mysql_connect,sentence,0,pwd);
    int pwd_id =atoi(pwd);
    Setpwd(queue,net_fd,pwd_id);
    
    char token[209]={0};
    Token(uname_buf,token);
    SendTrain(net_fd,token);
    //pthread_mutex_lock(&thread_pool->mutex);
    //EnQueue(&thread_pool->task_queue, net_fd, uid);
    //pthread_cond_signal(&thread_pool->cond);
    //pthread_mutex_unlock(&thread_pool->mutex);

    return 0;
}

int EventLoop(int epoll_fd, int listen_fd, ThreadPool *thread_pool,Main* Cmain){
    //短连接数据库连接，一定是先创建连接后再有子线程，所以不用锁
    printf("服务器启动成功\n");
    MYSQL *mysql_connect = (MYSQL *)calloc(1, sizeof(MYSQL));
    pthread_mutex_lock(&thread_pool->mutex);
    SqlConnectionInit(&mysql_connect);
    pthread_mutex_unlock(&thread_pool->mutex);
    time_t before = time(NULL);
    time_t now = time(NULL);
    int timenow =0;
    int timebefore = -1;
    int pair[1024];
    int ready_num = 0;
    memset(pair,-1,sizeof(pair));
    while(1){
        struct epoll_event ready_set[1024];
        now = time(NULL);
        if(now - before >=1)
        {
            timenow = (timenow+1)%TIME;
            before = now;
        }
        if(timenow + 1 < TIME)
        {
            timebefore = timenow +1;
        }else{
            timebefore = 0;
        }
        printf("%d,%d\n",timenow,timebefore);
        ready_num = epoll_wait(epoll_fd, ready_set, 1024, 1000);
        for(int i = 0; i < ready_num; ++i){
            if(ready_set[i].data.fd == listen_fd){
                //将未登录客户端的连接加入监听，等待该用户登录
                int net_fd = accept(listen_fd,NULL,NULL);
                printf("got one connection!,netfd = %d\n", net_fd);
                EpollAdd(epoll_fd, net_fd, EPOLLIN);
                EnMain(&(Cmain->mq[timenow]),net_fd);
                pair[net_fd]=timenow;
            }else if(ready_set[i].data.fd == exit_pipe[0]){
                ExitHandler(thread_pool);
            }else{
                //读取一次，获得command_id
                char command_id_buf[32] = {0};
                RecvTrain(ready_set[i].data.fd, command_id_buf);
                if(!strcmp(command_id_buf, "")){
                    printf("error command id:%s\n", command_id_buf);
                    EpollDel(epoll_fd, ready_set[i].data.fd);
                    close(ready_set[i].data.fd);
                    DeMain(&(Cmain->mq[pair[ready_set[i].data.fd]]),ready_set[i].data.fd);
                    pair[ready_set[i].data.fd]=-1;
                    break;
                }
                int command_id = atoi(command_id_buf);
                int cur_pwd = 0;
                int num = pair[ready_set[i].data.fd];
                Getpwd(&Cmain->mq[num],ready_set[i].data.fd,&cur_pwd);
                if(cur_pwd == 0)
                {
                    int sign = 0;
                    int login = 0;
                    Getsign(&Cmain->mq[num],ready_set[i].data.fd,&sign);
                    Getlogin(&Cmain->mq[num],ready_set[i].data.fd,&login);
                    ReEnMain(&Cmain->mq[num],&(Cmain->mq[timenow]),ready_set[i].data.fd);
                    pair[ready_set[i].data.fd]=timenow;
                    if(sign == 1)
                    {
                        ServerSignUpHandler(mysql_connect, ready_set[i].data.fd,&Cmain->mq[timenow]);
                    }
                    else if(login == 1)
                    {
                        printf("1\n");
                        ServerLogInHandler(mysql_connect, ready_set[i].data.fd, &(Cmain->mq[timenow]));
                    }
                    else if(sign == 2){
                        printf("接收用户名\n");
                        ServerSignUpHandler_before(ready_set[i].data.fd,&Cmain->mq[timenow]);
                        sign = 1;
                        Setsign(&Cmain->mq[timenow],ready_set[i].data.fd,sign);
                    }
                    else if(login == 2){
                        printf("接收用户名\n");
                        ServerLogInHandler_before(ready_set[i].data.fd,&Cmain->mq[timenow]);
                        login = 1;
                        Setlogin(&Cmain->mq[timenow],ready_set[i].data.fd,login);
                    }else if(command_id == LOGIN){
                        printf("开始登陆\n");
                        login = 2;
                        Setlogin(&Cmain->mq[timenow],ready_set[i].data.fd,login);
                    }
                    else if(command_id == SIGNUP){
                        printf("开始注册\n");
                        sign = 2;
                        Setsign(&Cmain->mq[timenow],ready_set[i].data.fd,sign);
                    }
                    else{
                        printf("error command id:%s :%d\n", command_id_buf, command_id);
                    }
                }
                else{
                    int uid = 0;
                    Getuid(&Cmain->mq[num],ready_set[i].data.fd,&uid);
                    ReEnMain(&Cmain->mq[num],&(Cmain->mq[timenow]),ready_set[i].data.fd);
                    pair[ready_set[i].data.fd]=timenow;
                    Dispatch(mysql_connect,ready_set[i].data.fd,&(Cmain->mq[timenow].rear->pwd_id),command_id,thread_pool,listen_fd,uid,epoll_fd);
                }
            }
        }
        DestoryQueue(&(Cmain->mq[timebefore]),pair,epoll_fd);
    }
    //关闭短连接数据库
    SqlConnectionClosed(&mysql_connect);
    free(mysql_connect);
    return 0;
}

int main(int argc, char *argv[]){
    //./server IP port threads_num
    ARGC_CHECK(argc, 4);

    //初始化优雅退出进程
    pipe(exit_pipe);
    if(fork() != 0){
        close(exit_pipe[0]);
        signal(SIGUSR1, UserSignalHandler);
        wait(NULL);
        printf("Parent is going to exit!\n");
        exit(0);
    }
    close(exit_pipe[1]);

    //创建目录
    char *netdisk_path = "Netdisk/";
    mkdir(netdisk_path, 0755);
    chdir(netdisk_path);

    //创建线程池
    ThreadPool thread_pool;
    ThreadPoolInit(&thread_pool, atoi(argv[3]));

    //创建监听套接字
    int listen_fd = 0;
    int ret_tcp_init = TcpInit(argv[1], argv[2], listen_type, &listen_fd);
    ERROR_CHECK(ret_tcp_init, 1, "TCP init");

    //创建epoll
    int epoll_fd = epoll_create(1);
    int ret_epoll_add = EpollAdd(epoll_fd, listen_fd, EPOLLIN);
    ERROR_CHECK(ret_epoll_add, -1, "epoll add");
    ret_epoll_add = EpollAdd(epoll_fd,exit_pipe[0], EPOLLIN);
    ERROR_CHECK(ret_epoll_add, -1, "epoll add");

    //创建Main
    Main Cmain;
    MainInit(&Cmain);
    //事件循环
    EventLoop(epoll_fd, listen_fd, &thread_pool,&Cmain);

    return 0;
}
